/*
PathJS - Copyright (c) 2011 Mike Trpcic, released under the MIT license.
 */
const window = window || global || {};

var Path = {
  // Need this specific version, because latest version is causing issue.
  version: "0.8.5",
  map: function (path) {
    if (Path.routes.defined.hasOwnProperty(path)) {
      return Path.routes.defined[path];
    } else {
      return new Path.core.route(path);
    }
  },
  root: function (path) {
    Path.routes.root = path;
  },
  rescue: function (fn) {
    Path.routes.rescue = fn;
  },
  history: {
    initial: {}, // Empty container for "Initial Popstate" checking variables.
    pushState: function (state, title, path) {
      if (Path.history.supported) {
        if (Path.dispatch(path)) {
          history.pushState(state, title, path);
        }
      } else {
        if (Path.history.fallback) {
          window.location.hash = "#" + path;
        }
      }
    },
    popState: function (event) {
      var initialPop =
        !Path.history.initial.popped &&
        location.href == Path.history.initial.URL;
      Path.history.initial.popped = true;
      if (initialPop) return;
      Path.dispatch(
        document.location.pathname === "/" ? "" : document.location.pathname
      );
    },
    listen: function (fallback) {
      Path.history.supported = !!(window.history && window.history.pushState);
      Path.history.fallback = fallback;

      if (Path.history.supported) {
        (Path.history.initial.popped = "state" in window.history),
          (Path.history.initial.URL = location.href);
        window.onpopstate = Path.history.popState;
      } else {
        if (Path.history.fallback) {
          for (route in Path.routes.defined) {
            if (route.charAt(0) != "#") {
              Path.routes.defined["#" + route] = Path.routes.defined[route];
              Path.routes.defined["#" + route].path = "#" + route;
            }
          }
          Path.listen();
        }
      }
    }
  },
  match: function (path, parameterize) {
    if (path === "") path = "/";
    var params = {},
      route = null,
      possible_routes,
      slice,
      i,
      j,
      compare,
      result;
    for (route in Path.routes.defined) {
      if (route !== null && route !== undefined) {
        route = Path.routes.defined[route];
        possible_routes = route.partition();
        for (j = 0; j < possible_routes.length; j++) {
          slice = possible_routes[j];
          compare = path;
          if (slice.search(/:/) > 0) {
            for (i = 0; i < slice.split("/").length; i++) {
              if (
                i < compare.split("/").length &&
                slice.split("/")[i].charAt(0) === ":"
              ) {
                params[slice.split("/")[i].replace(/:/, "")] = compare.split(
                  "/"
                )[i];
                result = compare.split("/");
                result[i] = slice.split("/")[i];
                compare = result.join("/");
              }
            }
          }
          if (slice === compare) {
            if (parameterize) {
              route.params = params;
            }
            return route;
          }
        }
      }
    }
    return null;
  },
  dispatch: function (passed_route) {
    var previous_route, matched_route;
    if (Path.routes.current !== passed_route) {
      Path.routes.previous = Path.routes.current;
      Path.routes.current = passed_route;
      matched_route = Path.match(passed_route, true);

      if (Path.routes.previous) {
        previous_route = Path.match(Path.routes.previous);
        if (previous_route !== null && previous_route.do_exit !== null) {
          previous_route.do_exit();
        }
      }

      if (matched_route !== null) {
        matched_route.run();
        return true;
      } else {
        if (Path.routes.rescue !== null) {
          Path.routes.rescue();
        }
      }
    }
  },
  listen: function () {
    var fn = function () {
      Path.dispatch(location.hash);
    };

    if (location.hash === "") {
      if (Path.routes.root !== null) {
        location.hash = Path.routes.root;
      }
    }

    // The 'document.documentMode' checks below ensure that PathJS fires the right events
    // even in IE "Quirks Mode".
    if (
      "onhashchange" in window &&
      (!document.documentMode || document.documentMode >= 8)
    ) {
      window.onhashchange = fn;
    } else {
      setInterval(fn, 50);
    }

    if (location.hash !== "") {
      Path.dispatch(location.hash);
    }
  },
  core: {
    route: function (path) {
      this.path = path;
      this.action = null;
      this.do_enter = [];
      this.do_exit = null;
      this.params = {};
      Path.routes.defined[path] = this;
    }
  },
  routes: {
    current: null,
    root: null,
    rescue: null,
    previous: null,
    defined: {}
  }
};

Path.core.route.prototype = {
  to: function (fn) {
    this.action = fn;
    return this;
  },
  enter: function (fns) {
    if (fns instanceof Array) {
      this.do_enter = this.do_enter.concat(fns);
    } else {
      this.do_enter.push(fns);
    }
    return this;
  },
  exit: function (fn) {
    this.do_exit = fn;
    return this;
  },
  partition: function () {
    var parts = [],
      options = [],
      re = /\(([^}]+?)\)/g,
      text,
      i;
    while ((text = re.exec(this.path))) {
      parts.push(text[1]);
    }
    options.push(this.path.split("(")[0]);
    for (i = 0; i < parts.length; i++) {
      options.push(options[options.length - 1] + parts[i]);
    }
    return options;
  },
  run: function () {
    var halt_execution = false,
      i,
      result,
      previous;

    if (Path.routes.defined[this.path].hasOwnProperty("do_enter")) {
      if (Path.routes.defined[this.path].do_enter.length > 0) {
        for (i = 0; i < Path.routes.defined[this.path].do_enter.length; i++) {
          result = Path.routes.defined[this.path].do_enter[i].apply(this, null);
          if (result === false) {
            halt_execution = true;
            break;
          }
        }
      }
    }
    if (!halt_execution) {
      Path.routes.defined[this.path].action();
    }
  }
};

export default Path;
